﻿using System.Reflection;
using Furion.DependencyInjection;
using Vboot.Core.Common;
using Vboot.Core.Framework;
using Vboot.Core.Framework.Security;
using Vboot.Core.Module.Sys;

namespace Vboot.Web.Init;


public class SysApiInit : ITransient
{
    private readonly SqlSugarRepository<SysApiMain> repo;

    public SysApiInit(SqlSugarRepository<SysApiMain> repo)
    {
        this.repo = repo;
    }

    public async Task Init()
    {
        List<Yurl> codeUrlList = GetScanList("Vboot.Core.dll");
        codeUrlList.AddRange(GetScanList("Vboot.Extend.dll"));
        codeUrlList.AddRange(GetScanList("Vboot.App.dll"));

        List<string> dbUrlList = repo.Context.Queryable<SysApiMain>()
            // .Where(t => t.type == "get")
            .Select(t => t.id)
            .ToList();

        //收集最大权限位及最大权限位的最大权限码
        string sql = "select max(u.pos) as \"pos\",max(u.code) as \"code\" from sys_api_main u where u.pos = (select max(uu.pos) from sys_api_main uu)";
        List<dynamic> maxList = repo.Context.Ado.SqlQuery<dynamic>(sql);
        int pos = 0;
        long code = 0;
        if (maxList != null && maxList.Count > 0 && maxList[0].pos != null)
        {
            SysApiMainCache.AUTHPOS =(int) maxList[0].pos;
            pos =(int) maxList[0].pos;
            code =(long) maxList[0].code;
        }

        //比较两个list得到insertList
        List<SysApiMain> insertList = new List<SysApiMain>();
        List<SysApiMain> updateList = new List<SysApiMain>();
        foreach (var codeUrl in codeUrlList)
        {
            bool flag = false;
            foreach (var dbUrl in dbUrlList)
            {
                if (codeUrl.id == dbUrl)
                {
                    flag = true;
                    break;
                }
            }

            if (!flag) //插入
            {
                if (codeUrl.type == null)
                {
                    var api = new SysApiMain();
                    api.id = codeUrl.id;
                    api.url = codeUrl.url;
                    api.pid = codeUrl.pid;
                    api.pos = 0;
                    api.code = 0;
                    api.avtag = true;
                    insertList.Add(api);
                }
                else
                {
                    if (code >= (1L << 62))
                    {
                        pos += 1;
                        SysApiMainCache.AUTHPOS++;
                        code = 1;
                    }
                    else
                    {
                        if (code == 0)
                        {
                            code = 1;
                        }
                        else
                        {
                            code <<= 1;
                        }
                    }

                    var api = new SysApiMain();
                    api.id = codeUrl.id;
                    api.url = codeUrl.url;
                    api.pid = codeUrl.pid;
                    api.type = codeUrl.type;
                    api.pos = pos;
                    api.code = code;
                    api.avtag = true;
                    insertList.Add(api);
                }
            }
            else //更新
            {
                var perm = new SysApiMain();
                perm.id = codeUrl.id;
                perm.url = codeUrl.url;
                perm.pid = codeUrl.pid;
                perm.type = codeUrl.type;
                perm.avtag = true;
                updateList.Add(perm);
            }
        }

        await repo.Context.Updateable<SysApiMain>().SetColumns(it => it.avtag == false).Where(it => it.avtag == true)
            .ExecuteCommandAsync();
        if (updateList.Count > 0)
        {
            await repo.UpdateRangeAsync(updateList);
        }

        if (insertList.Count > 0)
        {
            await repo.InsertRangeAsync(insertList);
        }

        string getsSql = "SELECT url,pos,code from sys_api_main where avtag= 1 and type='get'";
        // List<Yapi> cacheGets = repo.asSqlQuery<Yapi>(getsSql);
        List<Yapi> cacheGets = repo.Context.Ado.SqlQuery<Yapi>(getsSql);

        string postsSql = "SELECT url,pos,code from sys_api_main where avtag= 1 and type='post'";
        List<Yapi> cachePosts = repo.Context.Ado.SqlQuery<Yapi>(postsSql);

        string putsSql = "SELECT url,pos,code from sys_api_main where avtag= 1 and type='put'";
        List<Yapi> cachePuts = repo.Context.Ado.SqlQuery<Yapi>(putsSql);

        string deletesSql = "SELECT url,pos,code from sys_api_main where avtag= 1 and type='delete'";
        List<Yapi> cacheDeletes = repo.Context.Ado.SqlQuery<Yapi>(deletesSql);

        SysApiMainCache.GET_URLS = cacheGets.ToArray();
        SysApiMainCache.POST_URLS = cachePosts.ToArray();
        SysApiMainCache.PUT_URLS = cachePuts.ToArray();
        SysApiMainCache.DELETE_URLS = cacheDeletes.ToArray();

        await repo.Context.Updateable<SysOrgUser>().SetColumns(it => it.retag == false).Where(it => it.retag == true)
            .ExecuteCommandAsync();
    }


    //通过反射获取最新代码里所有需要认证的URL集合
    private List<Yurl> GetScanList(string searchDll)
    {
        var list = new List<Yurl>();
        var path = AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
        var coreAssemblies = System.IO.Directory.GetFiles(path, searchDll).Select(Assembly.LoadFrom)
            .ToArray();
        var coreModelTypes = coreAssemblies
            .SelectMany(a => a.DefinedTypes)
            .Select(type => type.AsType())
            .Where(x => x.IsClass && x.Name.EndsWith("Api")).ToList();
        coreModelTypes.ForEach(t =>
        {
            var classUrl = XstrUtil.RenameUrlCase(t.Name.Substring(0, t.Name.Length - 3));
            if (!"authc".Equals(classUrl)&&!"sys/api/role/to".Equals(classUrl)&&!classUrl.StartsWith("gen"))
            {
                var methodInfo = t.GetMethods();
                var topUrl = new Yurl
                {
                    id = classUrl.Replace("/", "-"),
                    url = "/" + classUrl,
                };
                if (topUrl.id.Contains('-'))
                {
                    topUrl.pid = topUrl.id.Split('-')[0];
                }

                list.Add(topUrl);

                foreach (MethodInfo mInfo in methodInfo)
                {
                    if ("GetType" != mInfo.Name && "ToString" != mInfo.Name && "Equals" != mInfo.Name &&
                        "GetHashCode" != mInfo.Name)
                    {
                        if (mInfo.Name.StartsWith("Get"))
                        {
                            // Console.WriteLine(mInfo.Name);
                            // Console.WriteLine(mInfo.CustomAttributes);
                            
                            var methodUrl = XstrUtil.RenameUrlCase(mInfo.Name.Substring(3));
                            var yurl = new Yurl
                            {
                                id = methodUrl == ""
                                    ? topUrl.id + ":get"
                                    : topUrl.id + "-" + methodUrl.Replace("/", "-") + ":get",
                                pid = topUrl.id,
                                type = "get",
                                url = methodUrl == "" ? topUrl.url : topUrl.url + "/" + methodUrl
                            };
                            // if (!mInfo.IsDefined(typeof(QueryParametersAttribute), true))
                            // {
                            //     yurl.
                            // }
                            list.Add(yurl);
                        }
                        else if (mInfo.Name.StartsWith("Post"))
                        {
                            var methodUrl = XstrUtil.RenameUrlCase(mInfo.Name.Substring(4));
                            var yurl = new Yurl
                            {
                                id = methodUrl == ""
                                    ? topUrl.id + ":post"
                                    : topUrl.id + "-" + methodUrl.Replace("/", "-") + ":post",
                                pid = topUrl.id,
                                type = "post",
                                url = methodUrl == "" ? topUrl.url : topUrl.url + "/" + methodUrl
                            };
                            list.Add(yurl);
                        }
                        else if (mInfo.Name.StartsWith("Put"))
                        {
                            var methodUrl = XstrUtil.RenameUrlCase(mInfo.Name.Substring(3));
                            var yurl = new Yurl
                            {
                                id = methodUrl == ""
                                    ? topUrl.id + ":put"
                                    : topUrl.id + "-" + methodUrl.Replace("/", "-") + ":put",
                                pid = topUrl.id,
                                type = "put",
                                url = methodUrl == "" ? topUrl.url : topUrl.url + "/" + methodUrl
                            };
                            list.Add(yurl);
                        }
                        else if (mInfo.Name.StartsWith("Delete"))
                        {
                            var methodUrl = XstrUtil.RenameUrlCase(mInfo.Name.Substring(6));
                            var yurl = new Yurl
                            {
                                id = methodUrl == ""
                                    ? topUrl.id + ":delete"
                                    : topUrl.id + "-" + methodUrl.Replace("/", "-") + ":delete",
                                pid = topUrl.id,
                                type = "delete",
                                url = methodUrl == "" ? topUrl.url : topUrl.url + "/" + methodUrl
                            };
                            list.Add(yurl);
                        }
                    }
                }
            }
        });

        HashSet<String> rootSet = new HashSet<String>();
        foreach (var yurl in list)
        {
            if (yurl.type == null && yurl.pid != null)
            {
                rootSet.Add(yurl.pid);
            }
        }

        foreach (var str in rootSet)
        {
            Yurl yurl = new Yurl();
            yurl.id = str;
            list.Add(yurl);
        }

        return list;
    }
}